using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Xml;
using System.Linq;
using CamQueryBuilder.SharepointList;

namespace RunCamlSample.QueryBuilders
{
    /// <summary>
    /// Create By: Nguyen Thanh Tung
    /// </summary>
    public class QueryProcceser : IQueryProcceser
    {
        private readonly Lists lists = new Lists();
        private string documentName;

        public QueryProcceser(string url)
        {
            lists.Credentials = CredentialCache.DefaultCredentials;
            lists.Url = url;
        }

        public QueryProcceser(Lists lists)
        {
            this.lists = lists;
        }

        public ICredentials Credentials
        {
            get { return lists.Credentials; }
            set { lists.Credentials = value; }
        }

        public ProccessContext CurrentContext { get; set; }

        public void LoadList(string name)
        {
            // get a list of all top level lists
            XmlNode allLists = lists.GetListCollection();

            // load into an XML document so we can use XPath to query content
            var allListsDoc = new XmlDocument();
            allListsDoc.LoadXml(allLists.OuterXml);
            var ns = new XmlNamespaceManager(allListsDoc.NameTable);
            ns.AddNamespace("d", allLists.NamespaceURI);

            // now get the GUID of the document library we are looking for
            XmlNode node = allListsDoc.SelectSingleNode("/d:Lists/d:List[@Title='" + name + "']", ns);

            documentName = name;

            CurrentContext = new ProccessContext(node);
        }

        public IList<string> GetAllField()
        {
            XmlNode xmlNode = lists.GetList(CurrentContext.GUID);
            XmlDocument document = new XmlDocument();
            document.LoadXml(xmlNode.OuterXml);
            XmlNodeList xmlNodeList = xmlNode.SelectNodes("//*");
            IList<string> result = new List<string>();
            //Console.WriteLine(xmlNodeList.Count);
            foreach (XmlNode node in xmlNodeList)
            {
                if (node.Attributes["Name"] != null)
                    result.Add(node.Attributes["Name"].Value);
            }
            return result.Distinct().ToList();
        }

        public ResultList GetListItems(QueryBuilder query, ViewBuilder viewBuilder, QueryOptionsBuilder options, int? rowLimit)
        {
            XmlNode listItems = lists.GetListItems(CurrentContext.GUID, null, query == null ? null : query.ToXmlNode(), viewBuilder == null ? null : viewBuilder.ToXmlNode(), rowLimit.ToString(), options == null ? null : options.ToXmlNode(), CurrentContext.WebId);

            XmlDocument xmlResultsDoc = new XmlDocument();
            xmlResultsDoc.LoadXml(listItems.OuterXml);

            var result = ResultList.Parser(listItems);
            if (result.Count > 0)
                xmlResultsDoc.Save("D:\\Projects\\discustion.xml");

            return result;
        }

        public void ExcuteFolder(string[] selectFields, int rowLimit, Action<ResultList> action)
        {
            int lastid = 0;
            QueryOptionsBuilder options = new QueryOptionsBuilder().Scope(Scope.All);
            options.Folder("");

            ResultList result;
            do
            {
                var builder = new QueryBuilder()
                            .And()
                                .Where("ID", QueryOperation.Gt, lastid.ToString(), "Counter")
                                .Where("ContentType", QueryOperation.Eq, "Folder", "Text")
                            .EndAnd()
                            .OrderBy("ID", true);

                result = GetListItems(builder, new ViewBuilder().AppendField(selectFields), options, rowLimit);

                if (result.Count > 0)
                {
                    action(result);
                    lastid = result.Lists.Select(c => c.Id).Max();
                }

            } while (result.Count > 0);
        }

        public void ExcuteItem(string[] selectFields, int rowLimit, Action<ResultList> action)
        {
            int lastid = 0;
            QueryOptionsBuilder options = new QueryOptionsBuilder().Scope(Scope.All);
            options.Folder("");

            ResultList result;
            do
            {
                var builder = new QueryBuilder()
                            .Where("ID", QueryOperation.Gt, lastid.ToString(), "Counter")
                            .OrderBy("ID", true);

                result = GetListItems(builder, new ViewBuilder().AppendField(selectFields), options, rowLimit);

                if (result.Count > 0)
                {
                    action(result);
                    lastid = result.Lists.Select(c => c.Id).Max();
                }

            } while (result.Count > 0);
        }

        public void UpdateListItem(BatchQueryBuilder query)
        {
            XmlNode result = lists.UpdateListItems(CurrentContext.GUID, query.ToXmlNode());
            //XmlDocument xmlResultsDoc = new XmlDocument();
            //xmlResultsDoc.LoadXml(result.OuterXml);
            //xmlResultsDoc.Save("D:\\Projects\\batchresutl.xml");
        }

        public void DeleteItems(string id)
        {
            BatchQueryBuilder query2 = new BatchQueryBuilder();
            query2.Method("1", Command.Delete).Fields(new List<FieldEx>
                                                         {
                                                             new FieldEx { Name = "ID", Value = id },
                                                         });

            query2.OnError(OnErrorType.Return);

            UpdateListItem(query2);
        }

        public void AddDiscussionBoardItem(NameValueCollection values, string body)
        {
            var sb = new StringBuilder();
            foreach (var key in values.AllKeys)
            {
                sb.AppendFormat("{0}: {1}\n", key, values[key]);
            }
            sb.Append("\n");
            sb.Append(body);
            byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
            lists.AddDiscussionBoardItem(CurrentContext.GUID, bytes);
        }

        public string GetLocalPath(string path)
        {
            string root = GetRootPath();
            return path.Replace(root.Remove(0, 1), string.Empty);
        }

        Dictionary<string, string> cache = new Dictionary<string, string>();

        public string GetDestinationId(string sourceId)
        {
            if (cache.ContainsKey(sourceId))
                return cache[sourceId];

            QueryOptionsBuilder options = new QueryOptionsBuilder()
                .Scope(Scope.All)
                .Folder("")
                .DateInUtc(true);

            var builder = new QueryBuilder()
                                .Where("COM_FSOFT_Citus_UNID", QueryOperation.Eq, sourceId, "Text")
                            .OrderBy("ID", true);

            ResultList result = GetListItems(builder, new ViewBuilder().AppendField(new[] { "ID" }), options, 3);

            if (result.Lists.Count > 0)
            {
                string id = result.Lists.First()["ID"];
                cache[sourceId] = id;
                return id;
            }
            return string.Empty;
        }

        Dictionary<string, string> cache2 = new Dictionary<string, string>();

        public string GetDestinationRef(string sourceId)
        {
            if (cache2.ContainsKey(sourceId))
                return cache2[sourceId];

            QueryOptionsBuilder options = new QueryOptionsBuilder()
                .Scope(Scope.All)
                .Folder("")
                .DateInUtc(true);

            var builder = new QueryBuilder()
                                .Where("COM_FSOFT_Citus_UNID", QueryOperation.Eq, sourceId, "Text")
                            .OrderBy("ID", true);

            ResultList result = GetListItems(builder, new ViewBuilder().AppendField(new[] { "FileRef" }), options, 3);

            if (result.Lists.Count > 0)
            {
                string id = result.Lists.First()["FileRef"].Split(new string[]{";#"}, StringSplitOptions.RemoveEmptyEntries)[1];
                cache2[sourceId] = id;
                return id;
            }
            return string.Empty;
        }

        public string GetRootPath()
        {
            string path = CurrentContext.CurrentNode.Attributes["DefaultViewUrl"].Value;

            if (string.IsNullOrEmpty(path)) return string.Empty;

            return path.Substring(0, path.IndexOf(documentName) + documentName.Length);
        }

        public void CreateFolder(IList<string> folders)
        {
            var q = new BatchQueryBuilder()
                .OnError(OnErrorType.Continue);
            //.RootFolder(parentFolder);

            int i = 1;
            foreach (var folder in folders)
            {
                string folderPath = folder;
                if (folder.StartsWith("/")) 
                    folderPath = folder.Remove(0, 1);

                q.Method((i++).ToString(), Command.New).Fields(new List<FieldEx>
                                                                   {
                                                                       new FieldEx { Name = "ID", Value = "New"},
                                                                       new FieldEx { Name = "FSObjType", Value = "1"},
                                                                       new FieldEx { Name = "BaseName", Value = folderPath }                                                                       
                                                                   });
            }

            XmlNode result = lists.UpdateListItems(CurrentContext.GUID, q.ToXmlNode());
        }
    }

    public class Base64Encoder
    {
        private const string mappings =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        private const string filler = "=";

        /// <summary>	Answer a string representing the Base 64 encoded form of the passed
        /// byte array
        /// 
        /// </summary>
        /// <returns> java.lang.String
        /// </returns>
        /// <param name="contents">byte[]
        /// </param>
        public static string Encode(string contents)
        {
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < contents.Length; i = i + 3)
            {
                if (result.Length == 76)
                {
                    result.Append("\n\r");
                }
                // output character 1
                result.Append(mappings[(contents[i] & 0xFC) >> 2]);
                // output character 2
                int c2 = (contents[i] & 0x03) << 4;
                if (i + 1 >= contents.Length)
                {
                    result.Append(mappings[c2]);
                    result.Append(filler);
                    result.Append(filler);
                    return result.ToString();
                }
                c2 |= ((contents[i + 1] & 0xF0) >> 4);
                result.Append(mappings[c2]);
                // output character 3
                int c3 = (contents[i + 1] & 0x0F) << 2;
                if (i + 2 >= contents.Length)
                {
                    result.Append(mappings[c3]);
                    result.Append(filler);
                    return result.ToString();
                }
                c3 |= ((contents[i + 2] & 0xC0) >> 6);
                result.Append(mappings[c3]);

                // output character 4
                result.Append(mappings[contents[i + 2] & 0x3F]);
            }
            return result.ToString();
        }
    }
}